查看原文
其他

软件应用丨多快好省地使用pandas分析大型数据集

数据Seminar 2023-01-01

The following article is from Python大数据分析 Author 费弗里

数据seminar每天18:00与您准时见面!



本文转载自公众号Python大数据分析



一、简介


pandas虽然是个非常流行的数据分析利器,但很多朋友在使用pandas处理较大规模的数据集的时候经常会反映pandas运算“慢”,且内存开销“大”。
特别是很多学生党在使用自己性能一般的笔记本尝试处理大型数据集时,往往会被捉襟见肘的算力所劝退。但其实只要掌握一定的pandas使用技巧,配置一般的机器也有能力hold住大型数据集的分析

图1

本文就将以真实数据集和运存16G的普通笔记本电脑为例,演示如何运用一系列策略实现多快好省地用pandas分析大型数据集。



二、pandas多快好省策略


我们使用到的数据集来自kaggle上的「TalkingData AdTracking Fraud Detection Challenge」竞赛(https://www.kaggle.com/c/talkingdata-adtracking-fraud-detection ),使用到其对应的训练集,这是一个大小有7.01G的csv文件。
下面我们将循序渐进地探索在内存开销和计算时间成本之间寻求平衡,首先我们不做任何优化,直接使用pandas的read_csv()来读取train.csv文件:
import pandas as pd
raw = pd.read_csv('train.csv')
# 查看数据框内存使用情况raw.memory_usage(deep=True)

图2

可以看到首先我们读入整个数据集所花费的时间达到了将近三分钟,且整个过程中因为中间各种临时变量的创建,一度快要撑爆我们16G的运行内存空间。

这样一来我们后续想要开展进一步的分析可是说是不可能的,因为随便一个小操作就有可能会因为中间过程大量的临时变量而撑爆内存,导致死机蓝屏,所以我们第一步要做的是降低数据框所占的内存:

指定数据类型以节省内存

因为pandas默认情况下读取数据集时各个字段确定数据类型时不会替你优化内存开销,比如我们下面利用参数nrows先读入数据集的前1000行试探着看看每个字段都是什么类型:
raw = pd.read_csv('train.csv', nrows=1000)raw.info()

图3

怪不得我们的数据集读进来会那么的大,原来所有的整数列都转换为了int64来存储,事实上我们原数据集中各个整数字段的取值范围根本不需要这么高的精度来存储,因此我们利用dtype参数来降低一些字段的数值精度:
raw = pd.read_csv('train.csv', nrows=1000, dtype={ 'ip': 'int32', 'app': 'int16', 'device': 'int16', 'os': 'int16', 'channel': 'int16', 'is_attributed': 'int8'              })raw.info()

图4

可以看到,在修改数据精度之后,前1000行数据集的内存大小被压缩了将近54.6%,这是个很大的进步,按照这个方法我们尝试着读入全量数据并查看其info()信息:

图5

可以看到随着我们对数据精度的优化,数据集所占内存有了非常可观的降低,使得我们开展进一步的数据分析更加顺畅,比如分组计数:
( raw    # 按照app和os分组计数        .groupby(['app', 'os'])        .agg({'ip': 'count'}))

图6

那如果数据集的数据类型没办法优化,那还有什么办法在不撑爆内存的情况下完成计算分析任务呢?

只读取需要的列

如果我们的分析过程并不需要用到原数据集中的所有列,那么就没必要全读进来,利用usecols参数来指定需要读入的字段名称:
raw = pd.read_csv('train.csv', usecols=['ip', 'app', 'os'])raw.info()

图7

可以看到,即使我们没有对数据精度进行优化,读进来的数据框大小也只有4.1个G,如果配合上数据精度优化效果会更好:

图8

如果有的情况下我们即使优化了数据精度又筛选了要读入的列,数据量依然很大的话,我们还可以以分块读入的方式来处理数据:

分块读取分析数据

利用chunksize参数,我们可以为指定的数据集创建分块读取IO流,每次最多读取设定的chunksize行数据,这样我们就可以把针对整个数据集的任务拆分为一个一个小任务最后再汇总结果:
from tqdm.notebook import tqdm
# 在降低数据精度及筛选指定列的情况下,以1千万行为块大小raw = pd.read_csv('train.csv', dtype={ 'ip': 'int32', 'app': 'int16', 'os': 'int16'                   },                                    usecols=['ip', 'app', 'os'],                                    chunksize=10000000)                  # 从raw中循环提取每个块并进行分组聚合,最后再汇总结果result = \( pd .concat([chunk .groupby(['app', 'os'], as_index=False) .agg({'ip': 'count'}) for chunk in tqdm(raw)])     .groupby(['app', 'os'])        .agg({'ip': 'sum'}))
result

图9

可以看到,利用分块读取处理的策略,从始至终我们都可以保持较低的内存负载压力,并且一样完成了所需的分析任务,同样的思想,如果你觉得上面分块处理的方式有些费事,那下面我们就来上大招:

利用dask替代pandas进行数据分析

dask相信很多朋友都有听说过,它的思想与上述的分块处理其实很接近,只不过更加简洁,且对系统资源的调度更加智能,从单机到集群,都可以轻松扩展伸缩。

图10

推荐使用conda install dask来安装dask相关组件,安装完成后,我们仅仅需要需要将import pandas as pd替换为import dask.dataframe as dd,其他的pandas主流API使用方式则完全兼容,帮助我们无缝地转换代码:

图11

可以看到整个读取过程只花费了313毫秒,这当然不是真的读进了内存,而是dask的延时加载技术,这样才有能力处理「超过内存范围的数据集」
接下来我们只需要像操纵pandas的数据对象一样正常书写代码,最后加上.compute(),dask便会基于前面搭建好的计算图进行正式的结果运算:
(    raw    #按照app和os分组计数        .groupby(['app', 'os'])        .agg({'ip': 'count'})        .compute() # 激活计算图)
并且dask会非常智能地调度系统资源,使得我们可以轻松跑满所有CPU:

图12

关于dask的更多知识可以移步官网自行学习:

https://docs.dask.org/en/latest/ 

图13

以上就是本文的全部内容,欢迎在评论区与我进行讨论~



点击阅读原文进入CCAD数据库

长按识别,添加小客服企业微信~



·END·


星标⭐我们不迷路!

想要文章及时到,文末“在看”少不了!


点击搜索你感兴趣的内容吧


往期推荐


数据呈现丨可视化版的《数据可视化指南——理论与实践》

机器学习丨刘忠宝,王宇飞,赵文娟 | 基于章节段落的学术文献结构功能识别方法研究

招聘丨冬天到来之前,加入企研数据

发布丨经管大类科研课程项目——经管科研能力提升计划第一季全新上线!

软件应用丨2020年入门数据分析选择Python还是SQL?七个常用操作对比!

统计计量丨计量经济学术语(下)

启研报告|疫情冲击下中国创业活跃度研究(一)




数据Seminar




这里是大数据、分析技术与学术研究的三叉路口



文丨Python大数据分析

推荐丨青酱

排版编辑丨青酱



    欢迎扫描👇二维码添加关注    

点击下方“阅读全文”了解更多

您可能也对以下帖子感兴趣

文章有问题?点此查看未经处理的缓存